/*
 *
 *      Copyright (c) 2018-2025, lengleng All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the pig4cloud.com developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: lengleng (wangiegie@gmail.com)
 *
 */

package com.haohan.cloud.scm.api.common.tree;


import cn.hutool.core.util.StrUtil;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.UtilityClass;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author lengleng
 * @date 2017年11月9日23:34:11
 */
@UtilityClass
public class TreeUtil {

    /**
     * 根节点id
     */
    public static String ROOT = "0";

    /**
     * 根节点名称
     */
    public static String ROOT_NAME = "无";

    /**
     * 两层循环实现建树
     *
     * @param treeNodes 传入的树节点列表
     * @return
     */
    public <E extends TreeNode> List<E> build(List<E> treeNodes, Object root) {

        List<E> trees = new ArrayList<>();

        for (E treeNode : treeNodes) {

            if (root.equals(treeNode.getParentId())) {
                trees.add(treeNode);
            }

            for (E it : treeNodes) {
                if (it.getParentId().equals(treeNode.getId())) {
                    if (treeNode.getChildren() == null) {
                        treeNode.setChildren(new ArrayList<>());
                    }
                    treeNode.add(it);
                }
            }
        }
        return trees;
    }

    /**
     * 使用递归方法建树
     *
     * @param treeNodes
     * @return
     */
    public <E extends TreeNode> List<E> buildByRecursive(List<E> treeNodes, Object root) {
        List<E> trees = new ArrayList<E>();
        for (E treeNode : treeNodes) {
            if (root.equals(treeNode.getParentId())) {
                trees.add(findChildren(treeNode, treeNodes));
            }
        }
        return trees;
    }

    /**
     * 递归查找子节点
     *
     * @param treeNodes
     * @return
     */
    public <T extends TreeNode> T findChildren(T treeNode, List<T> treeNodes) {
        for (T it : treeNodes) {
            if (treeNode.getId().equals(it.getParentId())) {
                if (treeNode.getChildren() == null) {
                    treeNode.setChildren(new ArrayList<>());
                }
                treeNode.add(findChildren(it, treeNodes));
            }
        }
        return treeNode;
    }

    /**
     * 递归 创建所有层级关系  TreeNode id 后代 parentId 祖先
     *
     * @param treeNodes 所有节点
     * @param <E>
     * @return
     */
    public <E extends TreeNode> List<TreeNode> buildAllRelation(List<E> treeNodes) {
        int nodeSize = treeNodes.size();
        int size = Math.max(8, nodeSize * 4 / 3 + 1);
        // map 所有根节点(parentId = ROOT)
        Map<String, RelationTreeNode> rootNodeMap = new HashMap<>(size);
        // map 节点id :节点
        Map<String, RelationTreeNode> nodeMap = treeNodes.stream()
                .map(item -> {
                    RelationTreeNode node = new RelationTreeNode(item);
                    rootNodeMap.put(node.getId(), node);
                    return node;
                })
                .collect(Collectors.toMap(RelationTreeNode::getId, Function.identity(), (oldValue, newValue) -> newValue));
        // 设置父级
        nodeMap.values().forEach(node -> {
            if (!ROOT.equals(node.getParentId())) {
                // 父级不存在时设为根节点
                RelationTreeNode parent = nodeMap.get(node.getParentId());
                if (null == parent) {
                    node.setParentId(ROOT);
                } else {
                    node.setParentNode(parent);
                    rootNodeMap.remove(node.getParentId());
                }
            }
        });
        // 递归设置 祖先及后代关系
        int leafSize = rootNodeMap.size();
        size = Math.max(16, (nodeSize - leafSize) * leafSize);
        Map<String, TreeNode> resultMap = new HashMap<>(size);
        rootNodeMap.values().forEach(node -> relationByRecursive(node, resultMap));
        return new ArrayList<>(resultMap.values());
    }

    /**
     * 递归设置 祖先及后代关系
     *
     * @param node
     * @param result TreeNode id 后代 parentId 祖先
     */
    private static void relationByRecursive(RelationTreeNode node, Map<String, TreeNode> result) {
        // 父节点为根节点  完成
        if (!ROOT.equals(node.getParentId())) {
            // 父节点不为根节点 设置关系
            relationByRecursive(node.getParentNode(), result);
            // 更新父节点集合
            Set<String> parentIdSet = node.getParentIdSet();
            parentIdSet.add(node.getParentId());
            parentIdSet.addAll(node.getParentNode().getParentIdSet());
            // 设置关系   id 后代 parentId 祖先
            parentIdSet.forEach(parentId -> {
                String key = parentId.concat(StrUtil.COLON).concat(node.getId());
                if (!result.containsKey(key)) {
                    TreeNode treeNode = new TreeNode();
                    treeNode.setId(node.getId());
                    treeNode.setParentId(parentId);
                    result.put(key, treeNode);
                }
            });
        }
    }

    @Data
    @EqualsAndHashCode(callSuper = true)
    @NoArgsConstructor
    private static class RelationTreeNode extends TreeNode<RelationTreeNode> {
        private static final long serialVersionUID = 1L;

        @ApiModelProperty(value = "父节点")
        private RelationTreeNode parentNode;

        @ApiModelProperty(value = "父节点id 集合")
        private Set<String> parentIdSet;

        public <T extends TreeNode> RelationTreeNode(T node) {
            this.id = node.id;
            this.parentId = node.parentId;
            this.parentIdSet = new HashSet<>(8);
        }
    }

}
